﻿#include "IBLLControlOperator.h"
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QElapsedTimer>
#include <QMutexLocker>


using namespace CVV::Interface;
IBLLControlOperator::IBLLControlOperator(QObject *parent)
    : QObject(parent)
    , m_bParallelReturn(true)
{

}

void IBLLControlOperator::initImageHeader(const QString &imageHeader, const cv::Mat &image)
{
    QMutexLocker locker(&m_mutex);
    m_strImageHeader = imageHeader;
    m_image = image;
}

IBLLControlOperator::~IBLLControlOperator()
{

}

void IBLLControlOperator::slotRunResult(const QString &puginCode,
                                        const QString &cellId,
                                        std::shared_ptr<CVV::Interface::IPluginParam> pResultOutput,
                                        bool bSuccess,
                                        bool bRuned)
{
    QMutexLocker locker(&m_mutex);
    QString strKey = QString("%1.%2").arg(puginCode).arg(cellId);
    if (!m_mapInOutParam.contains(strKey))
    {
        m_mapInOutParam.insert(strKey, pResultOutput);
    }

    emit sigRunResult(puginCode, cellId, pResultOutput, bSuccess, bRuned);
}

void IBLLControlOperator::slotParallelRunFinished()
{
    m_bParallelReturn = true;
}

bool IBLLControlOperator::iSPreNodeResultReturned(const QString &strPreId)
{
    if (strPreId.isEmpty())
    {
        return true;
    }
    QStringList lstPreId = strPreId.split(",");
    QStringList lstKeys;
    {
        QMutexLocker locker(&m_mutex);
        lstKeys = m_mapInOutParam.keys();
    }

    for (auto &preId : qAsConst(lstPreId))
    {
        bool bFind = false;
        for (auto &key : qAsConst(lstKeys))
        {
            QString strId = key.mid(key.lastIndexOf(".") + 1);
            if (preId == strId)
            {
                bFind = true;
                break;
            }
        }

        if (!bFind)
        {
            return false;
        }
    }

    return true;
}

void IBLLControlOperator::initParam(std::shared_ptr<IPluginParam> pResultOutput)
{
    if (!pResultOutput)
    {
        return;
    }

    QMultiMap<QString, QString> mapPathCode = pResultOutput->getSelectPathAssociationCode();
    for (auto itr = mapPathCode.begin(); itr != mapPathCode.end(); ++itr)
    {
        QString strMapKey = itr.key();
        QString strAssociatedCode = itr.value();
        if (!strAssociatedCode.isEmpty())
        {
            QStringList lstValue = strMapKey.split(",");
            pResultOutput->setProperty(strAssociatedCode.toStdString().c_str(), QVariant());        // 清空
            for (auto strVal : lstValue)
            {
                QStringList lstStr = strVal.split(".");
                if (lstStr.size() == 3)
                {
                    QString strKey = strVal.mid(0, strVal.lastIndexOf("."));
                    std::shared_ptr<IPluginParam> pPreParam = nullptr;
                    {
                        QMutexLocker locker(&m_mutex);
                        pPreParam = m_mapInOutParam.value(strKey);
                    }
                    if (pPreParam)
                    {
                        QString strCode = lstStr.last();
                        QVariant var;
                        if (pPreParam->hasDynamicProperty(strCode))
                        {
                            var = pPreParam->getDynamicPropertyValue(strCode);
                        }
                        else
                        {
                            var = pPreParam->property(strCode.toStdString().c_str());
                        }

                        if (lstStr.first() == "InputVectorIndex")
                        {
                            pResultOutput->setIsLoopParam(true);
                        }
                        pResultOutput->setProperty(strAssociatedCode.toStdString().c_str(), var);
                    }
                }
            }
        }
    }
}

bool IBLLControlOperator::runNodeList(QList<QList<IBLLNodeParam> > &lstNode, bool bIsLoop)
{
    auto itr = lstNode.begin();
    while (itr != lstNode.end()) {
        if (itr->isEmpty())
        {
            itr = lstNode.erase(itr);
            continue;
        }

        QString strFirstKey = QString("%1.%2").arg(itr->first().m_strPluginCode).arg(itr->first().m_strPluginId);
        QString stLastrKey = QString("%1.%2").arg(itr->last().m_strPluginCode).arg(itr->last().m_strPluginId);
        {
            QMutexLocker locker(&m_mutex);
            if (!bIsLoop
                    && m_mapInOutParam.contains(strFirstKey)
                    && m_mapInOutParam.contains(stLastrKey))       // 表示已经执行过了
            {
                itr = lstNode.erase(itr);
                continue;
            }
        }

        if (iSPreNodeResultReturned(itr->first().m_strPreId))
        {
            for (auto &param : *itr)
            {
                bool bSuccess = false, bRuned = false;
                QElapsedTimer t;
                t.start();
                if (param.m_pOutParam && !param.m_pOutParam->getIsModelData())       // 非模板数据才执行
                {
                    bRuned = true;
                    initParam(param.m_pOutParam);
                    if (param.m_pBLLOper)
                    {
                        param.m_pBLLOper->initParam(m_mapInOutParam);
                        bSuccess = param.m_pBLLOper->run(param.m_pOutParam);
                    }
                    else if (param.m_pAlgOper)
                    {
                        bSuccess = param.m_pAlgOper->RunOperator(param.m_pAlgParam, param.m_pOutParam);
                    }
                }
                else
                {
                    bSuccess = true;
                }

                if (param.m_pOutParam)
                {
                    param.m_pOutParam->setT_status(bSuccess ? CVV::Interface::RunStatus::RunSuccessfully : CVV::Interface::RunStatus::RunError);
                    param.m_pOutParam->setT_error(bSuccess ? "" : tr("run failed!"));
                    param.m_pOutParam->setT_elapsedTime(t.nsecsElapsed()/1000000.0);
                    //                param.m_pOutParam->setPreId(strPreId);
                    param.m_pOutParam->setT_id(param.m_strPluginId);
                    param.m_pOutParam->setT_code(param.m_strPluginCode);
                    param.m_pOutParam->setT_isSuccess(bSuccess);
                    //                param.m_pOutParam->setT_title(paEle.attribute("title"));
                }
                QString strKey = QString("%1.%2").arg(param.m_strPluginCode).arg(param.m_strPluginId);

                {
                    QMutexLocker locker(&m_mutex);
                    m_mapInOutParam.insert(strKey, param.m_pOutParam);
                }

                emit sigRunResult(param.m_strPluginCode, param.m_strPluginId, param.m_pOutParam, bSuccess, bRuned);

                qDebug() << "elapsed time:" << strKey << param.m_pOutParam->getT_elapsedTime() << "ms";
                if (!bSuccess)
                {
                    qWarning() << __FUNCTION__ << "run failed. pluginCode = " << param.m_strPluginCode << ", cellId = " << param.m_strPluginId;
                    return false;
                }
            }

            itr = lstNode.erase(itr);
        }
        else
        {
            ++itr;
        }

        qApp->processEvents();
        QThread::msleep(1);
    }

    return true;
}

bool IBLLControlOperator::runNodeList(QList<IBLLNodeParam> &lstNode, bool bIsLoop)
{

}

void IBLLControlOperator::noRunNodeIdAppendToResultMap(const QList<QList<IBLLNodeParam> > &lstNode)
{
    auto itr = lstNode.begin();
    while (itr != lstNode.end()) {
        for (auto &param : *itr)
        {
            QString strKey = QString("%1.%2").arg(param.m_strPluginCode).arg(param.m_strPluginId);

            {
                QMutexLocker locker(&m_mutex);
                m_mapInOutParam.insert(strKey, param.m_pOutParam);
            }

            emit sigRunResult(param.m_strPluginCode, param.m_strPluginId, param.m_pOutParam, true, false);
            noRunNodeIdAppendToResultMap(param.m_lstYesNode);
            noRunNodeIdAppendToResultMap(param.m_lstNoNode);
        }
        ++itr;
    }
}

void IBLLControlOperator::resetNodeList(QList<QList<IBLLNodeParam> > &lstNode)
{
    QMap<QString, QVariant> mapPrpperty;
    auto itr = lstNode.begin();
    while (itr != lstNode.end()) {
        for (auto &param : *itr)
        {
            if (param.m_pOutParam)
            {
                param.m_pOutParam->setDynamicPropertyValue(mapPrpperty);
            }
            resetNodeList(param.m_lstYesNode);
            resetNodeList(param.m_lstNoNode);
        }
        ++itr;
    }
}

bool IBLLControlOperator::runNode(const IBLLNodeParam &param)
{
    bool bSuccess = false;
    if (iSPreNodeResultReturned(param.m_strPreId))
    {
        bool bRuned = false;
        QElapsedTimer t;
        t.start();
        if (param.m_pOutParam && !param.m_pOutParam->getIsModelData())  // 不是模板数据才执行
        {
            bRuned = true;
            initParam(param.m_pOutParam);
            if (param.m_pBLLOper)
            {
                param.m_pBLLOper->initParam(m_mapInOutParam);
                if(param.m_isExternalInputPlugin && !m_strImageHeader.isEmpty() && !m_image.empty())
                {
                    param.m_pOutParam->setO_image(m_image);
                    param.m_pOutParam->setT_inputImage(m_image);
                    param.m_pOutParam->setO_imageHeader(m_strImageHeader);
                    bSuccess=true;
                }
                else
                {
                    bSuccess = param.m_pBLLOper->run(param.m_pOutParam);
                }
            }
            else if (param.m_pAlgOper)
            {
                bSuccess = param.m_pAlgOper->RunOperator(param.m_pAlgParam, param.m_pOutParam);
            }
        }
        else
        {
            bSuccess = true;
        }

        if (param.m_pOutParam)
        {
            param.m_pOutParam->setT_status(bSuccess ? CVV::Interface::RunStatus::RunSuccessfully : CVV::Interface::RunStatus::RunError);
            param.m_pOutParam->setT_error(bSuccess ? "" : tr("run failed!"));
            param.m_pOutParam->setT_elapsedTime(t.nsecsElapsed()/1000000.0);
            param.m_pOutParam->setT_id(param.m_strPluginId);
            param.m_pOutParam->setT_code(param.m_strPluginCode);
            param.m_pOutParam->setT_isSuccess(bSuccess);
        }

        QString strKey = QString("%1.%2").arg(param.m_strPluginCode).arg(param.m_strPluginId);
        {
            QMutexLocker locker(&m_mutex);
            m_mapInOutParam.insert(strKey, param.m_pOutParam);
        }

        emit sigRunResult(param.m_strPluginCode, param.m_strPluginId, param.m_pOutParam, bSuccess, bRuned);

        qDebug() << "elapsed time:" << strKey << param.m_pOutParam->getT_elapsedTime() << "ms";
        if (!bSuccess)
        {
            qWarning() << __FUNCTION__ << "run failed. pluginCode = " << param.m_strPluginCode << ", cellId = " << param.m_strPluginId;
            return false;
        }
    }

    return bSuccess;
}

QVariant IBLLControlOperator::getValue(const QString &strPath)
{
    QStringList lstStr = strPath.split(".");
    if (lstStr.size() == 3)
    {
        QString strKey = strPath.mid(0, strPath.lastIndexOf("."));
        std::shared_ptr<IPluginParam> pPreParam = nullptr;
        {
            QMutexLocker locker(&m_mutex);
            pPreParam = m_mapInOutParam.value(strKey);
        }

        if (pPreParam)
        {
            QVariant var;
            QString strCode = lstStr.last();
            if (pPreParam->hasDynamicProperty(strCode))
            {
                var = pPreParam->getDynamicPropertyValue(strCode);
            }
            else
            {
                var = pPreParam->property(strCode.toStdString().c_str());
            }

            return var;
        }
    }

    return QVariant();
}

